/**
 * 
 */
package cn.springsoter.core.tool.utils;

import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;

/**
 * AES对称加密/解密工具类：密钥长度128/192/256
 * 
 * 
 * @author kevin
 *
 */
public class AESUtil {
	private AESUtil() {}
	
	/**
	 * 加密算法：AES
	 */
	private static final String KEY_ALGORITHM = "AES";
	
	/**
	 * 加密算法"AES/CBC/PKCS5Padding" => 算法/模式/补码方式，CBC模式：
	 * 它需要一个随机数作为IV参数，这样对于同一份明文，每次生成的密文都不同
	 */
	private static final String KEY_ALGORITHM_CBC = "AES/CBC/PKCS5Padding";
	
	/**
	 * 加密算法"AES/ECB/PKCS5Padding" => 算法/模式/补码方式，ECB模式：
	 * ECB模式是最简单的AES加密模式，它只需要一个固定长度的密钥，固定的明文会生成固定的密文，这种一对一的加密方式会导致安全性降低
	 */
	private static final String KEY_ALGORITHM_ECB = "AES/ECB/PKCS5Padding";
	
	
	/**
	 * (CBC模式)获取base64的加密字符串
	 * 
	 * @param key
	 * @param input
	 * @return
	 * @throws GeneralSecurityException
	 */
	public static String encryptByCBCBase64(byte[] key, byte[] input) throws GeneralSecurityException {
		return Base64.encodeBase64String(encryptByCBC(key, input));
	}
	
	/**
	 * (CBC模式)解密成明文字符串
	 * 
	 * @param key
	 * @param input
	 * @return
	 * @throws GeneralSecurityException
	 * @throws UnsupportedEncodingException
	 */
	public static String decryptByCBCString(byte[] key, String inputBas64) throws GeneralSecurityException, UnsupportedEncodingException {
		if(!Base64.isBase64(inputBas64) || ArrayUtils.isEmpty(key)) {
			//如果不是base64字符串，则直接返回传入的值
			return inputBas64;
		}
		return new String(decryptByCBC(key, Base64.decodeBase64(inputBas64)), "UTF-8");
	}
	
	/**
	 * (CBC模式)加密
	 * 
	 * @param key		密钥
	 * @param input
	 * @return binary	二进制流
	 * @throws GeneralSecurityException
	 */
    public static byte[] encryptByCBC(byte[] key, byte[] input) throws GeneralSecurityException {
    	if(ArrayUtils.isEmpty(key)) {
    		return input;
    	}
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_CBC);
        SecretKeySpec keySpec = new SecretKeySpec(key, KEY_ALGORITHM);
        // CBC模式需要生成一个16 bytes的initialization vector:
        SecureRandom sr = SecureRandom.getInstanceStrong();
        byte[] iv = sr.generateSeed(16);
        IvParameterSpec ivps = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivps);
        byte[] data = cipher.doFinal(input);
        // IV不需要保密，把IV和密文一起返回:
        return join(iv, data);
    }

    /**
     * (CBC模式)解密
     * 
     * @param key		密钥
     * @param input
     * @return binary	二进制流
     * @throws GeneralSecurityException
     */
    public static byte[] decryptByCBC(byte[] key, byte[] input) throws GeneralSecurityException {
    	if(ArrayUtils.isEmpty(key)) {
    		return input;
    	}
        // 把input分割成IV和密文:
        byte[] iv = new byte[16];
        byte[] data = new byte[input.length - 16];
        System.arraycopy(input, 0, iv, 0, 16);
        System.arraycopy(input, 16, data, 0, data.length);
        // 解密:
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_CBC);
        SecretKeySpec keySpec = new SecretKeySpec(key, KEY_ALGORITHM);
        IvParameterSpec ivps = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps);
        return cipher.doFinal(data);
    }

    public static byte[] join(byte[] bs1, byte[] bs2) {
        byte[] r = new byte[bs1.length + bs2.length];
        System.arraycopy(bs1, 0, r, 0, bs1.length);
        System.arraycopy(bs2, 0, r, bs1.length, bs2.length);
        return r;
    }
    
    //===============================ECB模式====================================
    
    /**
	 * (ECB模式)获取base64的加密字符串
	 * 
	 * @param key
	 * @param input
	 * @return
	 * @throws GeneralSecurityException
	 */
	public static String encryptByECBBase64(byte[] key, byte[] input) throws GeneralSecurityException {
		return Base64.encodeBase64String(encryptByECB(key, input));
	}
	
	/**
	 * (ECB模式)解密成明文字符串
	 * 
	 * @param key
	 * @param input
	 * @return
	 * @throws GeneralSecurityException
	 * @throws UnsupportedEncodingException
	 */
	public static String decryptByECBString(byte[] key, String inputBas64) throws GeneralSecurityException, UnsupportedEncodingException {
		if(!Base64.isBase64(inputBas64) || ArrayUtils.isEmpty(key)) {
			//如果不是base64字符串，则直接返回传入的值
			return inputBas64;
		}
		return new String(decryptByECB(key, Base64.decodeBase64(inputBas64)), "UTF-8");
	}
    
    /**
     * (ECB模式)加密
     * 
     * @param key
     * @param input
     * @return
     * @throws GeneralSecurityException
     */
    public static byte[] encryptByECB(byte[] key, byte[] input) throws GeneralSecurityException {
    	if(ArrayUtils.isEmpty(key)) {
    		return input;
    	}
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_ECB);
        SecretKey keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        return cipher.doFinal(input);
    }

    /**
     * (ECB模式)解密
     * 
     * @param key
     * @param input
     * @return
     * @throws GeneralSecurityException
     */
    public static byte[] decryptByECB(byte[] key, byte[] input) throws GeneralSecurityException {
    	if(ArrayUtils.isEmpty(key)) {
    		return input;
    	}
        Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_ECB);
        SecretKey keySpec = new SecretKeySpec(key, KEY_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        return cipher.doFinal(input);
    }
}
